home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
fslcl
/
fslclLookup.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
83KB
|
2,516 lines
/*
* fslclLookup.c --
*
* The routines in the module manage the directory structure.
* The top level loop is in FslclLookup, and it is the workhorse
* of the Local Domain that is called by procedures like FslclOpen.
* Files and directories are also created, deleted, and renamed
* directly (or indirectly) through FslclLookup.
*
* Support for heterogenous systems is done here by expanding "$MACHINE"
* in pathnames to a string like "sun3" or "spur".
*
* Copyright 1987 Regents of the University of California
* All rights reserved.
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/fslcl/fslclLookup.c,v 9.27 92/08/10 17:28:30 mgbaker Exp $ SPRITE (Berkeley)";
#endif not lint
#include <sprite.h>
#include <fs.h>
#include <fsconsist.h>
#include <fsutil.h>
#include <fsNameOps.h>
#include <fsprefix.h>
#include <fsdm.h>
#include <fslclInt.h>
#include <fslcl.h>
#include <fslclNameHash.h>
#include <fscache.h>
#include <fsStat.h>
#include <net.h>
#include <vm.h>
#include <string.h>
#include <proc.h>
#include <dbg.h>
#include <fsrecov.h>
#include <rpc.h>
#include <recov.h>
int fsCompacts; /* The number of times a directory block was so
* fragmented that we could have compacted it to
* make room for a new entry in the directory */
/*
* A cache of recently seen pathname components is kept in a hash table.
* The hash table gets initialized in Fsdm_AttachDisk after the first disk
* gets attached.
* The name caching can be disabled by setting the fslclNameCaching flag to FALSE.
*/
FslclHashTable fslclNameTable;
FslclHashTable *fslclNameTablePtr = (FslclHashTable *)NIL;
Boolean fslclNameCaching = TRUE;
int fslclNameHashSize = FSLCL_NAME_HASH_SIZE;
/*
* Forward Declarations.
*/
static ReturnStatus FindComponent _ARGS_((Fsio_FileIOHandle *parentHandlePtr,
char *component, int compLen, Boolean isDotDot,
Fsio_FileIOHandle **curHandlePtrPtr, int *dirOffsetPtr));
static ReturnStatus InsertComponent _ARGS_((Fsio_FileIOHandle *curHandlePtr,
char *component, int compLen, int fileNumber,
int *dirOffsetPtr));
static ReturnStatus DeleteComponent _ARGS_((Fsio_FileIOHandle *parentHandlePtr,
char *component, int compLen, int *dirOffsetPtr));
static ReturnStatus ExpandLink _ARGS_((Fsio_FileIOHandle *curHandlePtr,
char *curCharPtr, int offset, char nameBuffer[]));
static ReturnStatus GetHandle _ARGS_((int fileNumber,
Fsdm_FileDescriptor *newDescPtr,
Fsio_FileIOHandle *curHandlePtr, char *name,
Fsio_FileIOHandle **newHandlePtrPtr));
static ReturnStatus CreateFile _ARGS_((Fsdm_Domain *domainPtr,
Fsio_FileIOHandle *parentHandlePtr, char *component,
int compLen, int fileNumber, int type, int permissions,
Fs_UserIDs *idPtr, Fsio_FileIOHandle **curHandlePtrPtr,
int *dirOffsetPtr));
static ReturnStatus WriteNewDirectory _ARGS_((Fsio_FileIOHandle *curHandlePtr,
Fsio_FileIOHandle *parentHandlePtr));
static ReturnStatus LinkFile _ARGS_((Fsio_FileIOHandle *parentHandlePtr,
char *component, int compLen, int fileNumber, int logOp,
Fsio_FileIOHandle **curHandlePtrPtr, int clientID));
static ReturnStatus OkToMoveDirectory _ARGS_((
Fsio_FileIOHandle *newParentHandlePtr,
Fsio_FileIOHandle *curHandlePtr));
static ReturnStatus MoveDirectory _ARGS_((Time *modTimePtr,
Fsio_FileIOHandle *newParentHandlePtr,
Fsio_FileIOHandle *curHandlePtr));
static ReturnStatus GetParentNumber _ARGS_((Fsio_FileIOHandle *curHandlePtr,
int *parentNumberPtr));
static ReturnStatus SetParentNumber _ARGS_((Fsio_FileIOHandle *curHandlePtr,
int newParentNumber));
static ReturnStatus DeleteFileName _ARGS_((Fsio_FileIOHandle *parentHandlePtr,
Fsio_FileIOHandle *curHandlePtr, char *component, int compLen,
int forRename, Fs_UserIDs *idPtr, int logOp, int clientID));
static void CloseDeletedFile _ARGS_((Fsio_FileIOHandle **parentHandlePtrPtr,
Fsio_FileIOHandle **curHandlePtrPtr));
static Boolean DirectoryEmpty _ARGS_((Fsio_FileIOHandle *handlePtr));
static ReturnStatus CheckPermissions _ARGS_((Fsio_FileIOHandle *handlePtr,
int useFlags, Fs_UserIDs *idPtr, int type));
static ReturnStatus CacheDirBlockWrite _ARGS_((Fsio_FileIOHandle *handlePtr,
Fscache_Block *blockPtr, int blockNum, int length));
/*
*----------------------------------------------------------------------
*
* FslclLookup --
*
* The guts of local file name lookup. This does a recursive
* directory lookup of a file pathname. The success of the lookup
* depends on useFlags and the type. The process needs to
* have read permission along the path, and other permissions on
* the target file itself according to useFlags. The type of the
* target file has to agree with the type parameter.
*
* The major and minor fields of the Fs_FileID for local files correspond
* to the domain and fileNumber, respectively, of a file. The domain
* is an index into the set of active domains (disks), and the fileNumber
* is an index into the array of file descriptors on disk.
*
* Results:
* If SUCCESS is returned then *handlePtrPtr contains a valid file
* handle. This handle should be released with FsLocalClose.
* If FS_LOOKUP_REDIRECT is returned then **newNameInfoPtrPtr contains
* the new file name that the client takes back to its prefix table.
*
* Side effects:
* After a successful lookup the returned handle is locked and has
* another reference to it. Also, the domain in which the file was
* found has an extra reference that needs to be released with
* Fsdm_DomainRelease as soon as our caller is finished with the handle.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FslclLookup(prefixHdrPtr, relativeName, rootIDPtr, useFlags, type, clientID,
idPtr, permissions, fileNumber, handlePtrPtr, newNameInfoPtrPtr)
Fs_HandleHeader *prefixHdrPtr; /* Handle from the prefix table or
* the current working directory */
char *relativeName; /* Name to lookup relative to the
* file indicated by prefixHandlePtr */
Fs_FileID *rootIDPtr; /* File ID of the root of the domain */
int useFlags; /* FS_READ|FS_WRITE|FS_EXECUTE,
* FS_CREATE|FS_EXCLUSIVE, FS_OWNER,
* FS_LINK, FS_FOLLOW (links) */
int type; /* File type which to succeed on. If
* this is FS_FILE, then any type will
* work. */
int clientID; /* Host ID of the client doing the open.
* Require to properly expand $MACHINE
* in pathnames */
Fs_UserIDs *idPtr; /* User and group IDs */
int permissions; /* Permission bits to use on a newly
* created file. */
int fileNumber; /* File number to link to if FS_LINK
* useFlag is present */
Fsio_FileIOHandle **handlePtrPtr; /* Result, the handle for the file.
* This is returned locked. Also,
* its domain has a reference which
* needs to be released. */
Fs_RedirectInfo **newNameInfoPtrPtr; /* Redirect Result, the pathname left
* after it leaves our domain */
{
register char *curCharPtr; /* Pointer into the path name */
Fsio_FileIOHandle *parentHandlePtr; /* Handle for parent dir. */
register char *compPtr; /* Pointer into component. */
register ReturnStatus status = SUCCESS;
register int compLen = 0; /* The length of component */
Fsio_FileIOHandle *curHandlePtr; /* Handle for the current spot in
* the directory structure */
Fsdm_Domain *domainPtr; /* Domain of the lookup */
char component[FS_MAX_NAME_LENGTH]; /* One component of the path name */
char *newNameBuffer; /* Extra buffer used after a symbolic
* link has been expanded */
int numLinks = 0; /* The number of links that have been
* expanded. Used to check against
* loops. */
int logOp; /* Dir log operation. */
int dirFileNumber; /* File number od directory being
* operated on. */
int dirOffset; /* Offset of directory entry in the
* directory being operated on. */
ClientData logClientData; /* Client data for directory change
* logging. */
ClientData recovLogClientData = (ClientData) 0;
/* Client data for directory change
* logging in the recovery system. */
/*
* Get a handle on the domain of the file. This is needed for disk I/O.
* Remember that the <major> field of the fileID is a domain number.
*/
domainPtr = Fsdm_DomainFetch(prefixHdrPtr->fileID.major, FALSE);
if (domainPtr == (Fsdm_Domain *)NIL) {
return(FS_DOMAIN_UNAVAILABLE);
}
if (prefixHdrPtr->fileID.type != FSIO_LCL_FILE_STREAM) {
printf("FslclLookup, bad prefix handle type <%d> for {%s}%s\n",
prefixHdrPtr->fileID.type,
Fsutil_HandleName(prefixHdrPtr), relativeName);
return(FS_DOMAIN_UNAVAILABLE);
}
/*
* Duplicate the prefixHandle into the handle for the current point
* in the directory. This locks and ups the reference count on the handle.
*/
curCharPtr = relativeName;
curHandlePtr = Fsutil_HandleDupType(Fsio_FileIOHandle, prefixHdrPtr);
parentHandlePtr = (Fsio_FileIOHandle *)NIL;
newNameBuffer = (char *)NIL;
/*
* Loop through the pathname expanding links and checking permissions.
* Creations and deletions are handled after this loop.
*/
fs_Stats.lookup.number++;
while (*curCharPtr != '\0' && status == SUCCESS) {
status = CheckPermissions(curHandlePtr, FS_READ, idPtr, FS_DIRECTORY);
if (status != SUCCESS) {
break;
}
/*
* Get the next component. We make a special check here
* for "$MACHINE" embedded in the pathname. This gets expanded
* to a machine type string, i.e. "sun3" or "spur", sort of like
* a symbolic link. The value is dependent on the ID of the client
* doing the open. For the local host we use a compiled in string so
* we can bootstrap ok, and for other clients we get the machine
* type string from the net module. (why net? why not...
* Net_InstallRoute installs a host's name and machine type.)
*/
#define SPECIAL "$MACHINE"
#define SPECIAL_LEN 8
compPtr = component;
while (*curCharPtr != '/' && *curCharPtr != '\0') {
if (*curCharPtr == '$' &&
(strncmp(curCharPtr, SPECIAL, SPECIAL_LEN) == 0)) {
char machTypeBuffer[32];
char *machType;
fs_Stats.lookup.numSpecial++;
if (clientID == rpc_SpriteID) {
/*
* Can't count on the net stuff being setup for ourselves
* as that is done via a user program way after bootting.
* Instead, use a compiled in string. This is important
* when opening "/initSprite", which is a link to
* "/initSprite.$MACHINE", when running on the root server.
*/
machType = mach_MachineType;
} else {
Net_SpriteIDToMachType(clientID, 32, machTypeBuffer);
if (*machTypeBuffer == '\0') {
printf(
"FslclLookup, no machine type for client %d\n",
clientID);
machType = "unknown";
} else {
machType = machTypeBuffer;
}
}
while (*machType != '\0') {
*compPtr++ = *machType++;
if (compPtr - component >= FS_MAX_NAME_LENGTH) {
status = FS_INVALID_ARG;
goto endScan;
}
}
curCharPtr += SPECIAL_LEN;
#undef SPECIAL
#undef SPECIAL_LEN
} else {
*compPtr++ = *curCharPtr++;
}
if (compPtr - component >= FS_MAX_NAME_LENGTH) {
status = FS_INVALID_ARG;
goto endScan;
}
}
fs_Stats.lookup.numComponents++;
*compPtr = '\0';
compLen = compPtr - component;
/*
* Skip intermediate and trailing slashes so that *curCharPtr
* is Null when 'component' has the last component of the name.
*/
while (*curCharPtr == '/') {
curCharPtr++;
}
/*
* There are three cases for what the next component is:
* a sub-directory (or file), dot, and dot-dot.
*/
if ((compLen == 2) && component[0] == '.' && component[1] == '.') {
/*
* Going to the parent directory ".."
*/
if (curHandlePtr->hdr.fileID.minor == rootIDPtr->minor) {
/*
* We are falling off the top of the domain. Make the
* remaining part of the filename, including "../",
* available to our caller so it can go back to the prefix
* table. Setting the prefixLength to zero indicates
* there is no prefix information in this LOOKUP_REDIRECT
*/
*newNameInfoPtrPtr = mnew(Fs_RedirectInfo);
(*newNameInfoPtrPtr)->prefixLength = 0;
(void)strcpy((*newNameInfoPtrPtr)->fileName, "../");
(void)strcat((*newNameInfoPtrPtr)->fileName, curCharPtr);
fs_Stats.lookup.parent++;
status = FS_LOOKUP_REDIRECT;
} else {
/*
* Advance curHandlePtr to the parent, "..".
* FindComponent will unlock its parentHandlePtr (the directory
* containing "..") after scanning it but before grabbing
* the handle for "..". This prevents deadlock with another
* process descending the other way along the path.
* We then nuke our own "parent" because it really isn't a
* parent anymore.
*/
if (parentHandlePtr != (Fsio_FileIOHandle *)NIL) {
Fsutil_HandleRelease(parentHandlePtr, TRUE);
}
parentHandlePtr = curHandlePtr;
status = FindComponent(parentHandlePtr, component, compLen,
TRUE, &curHandlePtr, &dirOffset);
/*
* Release the handle while being careful about its lock.
* The parent handle is normally aready unlocked by
* FindComponent, unless the directory is corrupted.
*/
Fsutil_HandleRelease(parentHandlePtr, (status != SUCCESS));
parentHandlePtr = (Fsio_FileIOHandle *)NIL;
}
} else if ((compLen == 1) && component[0] == '.') {
/*
* Just hang tight with . (dot) because we already
* have a locked handle for it.
*/
} else {
/*
* Advance to the next component and keep the handle on
* the parent locked so we can do deletes and creates.
*/
if (parentHandlePtr != (Fsio_FileIOHandle *)NIL) {
Fsutil_HandleRelease(parentHandlePtr, TRUE);
}
parentHandlePtr = curHandlePtr;
status = FindComponent(parentHandlePtr, component, compLen, FALSE,
&curHandlePtr, &dirOffset);
}
/*
* At this point we have a locked handle on the current point
* in the lookup, and perhaps have a locked handle on the parent.
* Links are expanded now so we know whether or not the
* lookup is completed. On the last component, we only
* expand the link if the FS_FOLLOW flag is present.
*/
if ((status == SUCCESS) &&
((*curCharPtr != '\0') || (useFlags & FS_FOLLOW)) &&
((curHandlePtr->descPtr->fileType == FS_SYMBOLIC_LINK ||
curHandlePtr->descPtr->fileType == FS_REMOTE_LINK))) {
numLinks++;
fs_Stats.lookup.symlinks++;
if (numLinks > FS_MAX_LINKS) {
status = FS_NAME_LOOP;
} else {
/*
* An extra buffer is used because the caller probably only
* has a buffer just big enough for the name.
*/
int offset; /* Distance of existing name from
* the start of its buffer */
if (newNameBuffer == (char *)NIL) {
offset = (int)curCharPtr - (int)relativeName;
newNameBuffer = (char *)malloc(FS_MAX_PATH_NAME_LENGTH);
} else {
offset = (int)curCharPtr - (int)newNameBuffer;
}
status = ExpandLink(curHandlePtr, curCharPtr, offset,
newNameBuffer);
if (status == FS_FILE_NOT_FOUND) {
printf( "FslclLookup, empty link \"%s\"\n",
relativeName);
}
curCharPtr = newNameBuffer;
}
if (status == SUCCESS) {
/*
* (Note: One could enforce permissions on links here.)
* If the link is back to the root we have to REDIRECT,
* otherwise retreat the current point in the lookup to
* the parent directory before continuing.
*/
if (*curCharPtr == '/') {
*newNameInfoPtrPtr = mnew(Fs_RedirectInfo);
(void)strcpy((*newNameInfoPtrPtr)->fileName, curCharPtr);
status = FS_LOOKUP_REDIRECT;
/*
* Return the length of the prefix indicated by
* a remote link, zero means no prefix.
*/
if (curHandlePtr->descPtr->fileType == FS_REMOTE_LINK) {
fs_Stats.lookup.remote++;
(*newNameInfoPtrPtr)->prefixLength =
curHandlePtr->descPtr->lastByte;
} else {
fs_Stats.lookup.redirect++;
(*newNameInfoPtrPtr)->prefixLength = 0;
}
} else if (parentHandlePtr != (Fsio_FileIOHandle *)NIL) {
Fsutil_HandleRelease(curHandlePtr, TRUE);
curHandlePtr = parentHandlePtr;
parentHandlePtr = (Fsio_FileIOHandle *)NIL;
status = SUCCESS;
} else {
panic( "No parent after link");
status = FS_INVALID_ARG;
}
}
}
}
endScan:
if ((status == SUCCESS) ||
((status == FS_FILE_NOT_FOUND) && (*curCharPtr == '\0'))) {
/*
* Done with the lookup. Determine the type of the file once
* we have a handle for it if its type is not already set from the
* file descriptor. Process creates, links, and deletes.
*/
switch(useFlags & (FS_CREATE|FS_DELETE|FS_LINK)) {
case 0:
if (status == SUCCESS) {
/*
* Normal lookup completion.
*/
status = CheckPermissions(curHandlePtr, useFlags, idPtr,
type);
}
break;
case FS_CREATE:
fs_Stats.lookup.forCreate++;
if (status == SUCCESS && (useFlags & FS_EXCLUSIVE)) {
/*
* FS_EXCLUSIVE and FS_CREATE means that the file
* cannot already exist.
*/
status = FS_FILE_EXISTS;
} else if (status == FS_FILE_NOT_FOUND) {
/*
* 'component' is the last part of a pathname for a
* file we need to create and that doesn't exist.
* Check for write permission in the parent directory,
* choose a fileNumber for the new file, and then
* create the file itself.
*/
int newFileNumber;
int nearbyFile;
status = CheckPermissions(parentHandlePtr, FS_WRITE, idPtr,
FS_DIRECTORY);
if (status == SUCCESS) {
int logOp;
dirFileNumber = parentHandlePtr->hdr.fileID.minor;
newFileNumber = -1;
dirOffset = -1;
if (type == FS_DIRECTORY) {
logOp = FSDM_LOG_CREATE|FSDM_LOG_IS_DIRECTORY;
nearbyFile = -1;
} else {
logOp = FSDM_LOG_CREATE;
nearbyFile = dirFileNumber;
}
logClientData = Fsdm_DirOpStart(logOp,
parentHandlePtr, dirOffset,
component, compLen,
newFileNumber, type,
(Fsdm_FileDescriptor *) NIL);
if (recov_Transparent && clientID != rpc_SpriteID) {
recovLogClientData = Fsrecov_DirOpStart(logOp,
parentHandlePtr, dirOffset,
component, compLen,
newFileNumber, type,
(Fsdm_FileDescriptor *) NIL);
}
status = Fsdm_GetNewFileNumber(domainPtr, nearbyFile,
&newFileNumber);
if (status == SUCCESS) {
status = CreateFile(domainPtr, parentHandlePtr,
component, compLen, newFileNumber, type,
permissions, idPtr, &curHandlePtr,
&dirOffset);
if (status != SUCCESS) {
(void)Fsdm_FreeFileNumber(domainPtr,
newFileNumber);
}
}
if (status == SUCCESS) {
Fsdm_DirOpEnd(logOp,
parentHandlePtr, dirOffset,
component, compLen, newFileNumber, type,
curHandlePtr->descPtr, logClientData,
status);
if (recov_Transparent && clientID != rpc_SpriteID) {
Fsrecov_DirOpEnd(logOp,
parentHandlePtr, dirOffset,
component, compLen, newFileNumber, type,
curHandlePtr->descPtr,
recovLogClientData, status);
}
} else {
Fsdm_DirOpEnd(logOp,
parentHandlePtr, dirOffset,
component, compLen, newFileNumber, type,
(Fsdm_FileDescriptor *) NIL,
logClientData, status);
if (recov_Transparent && clientID != rpc_SpriteID) {
Fsrecov_DirOpEnd(logOp,
parentHandlePtr, dirOffset,
component, compLen, newFileNumber,
type, (Fsdm_FileDescriptor *) NIL,
recovLogClientData, status);
}
}
}
} else {
/*
* If the file exists, it's like a normal lookup completion
* and we have to check permissions.
*/
status = CheckPermissions(curHandlePtr, useFlags, idPtr,
type);
}
if (status == SUCCESS) {
(void)Fsdm_FileDescStore(curHandlePtr, FALSE);
}
break;
case FS_LINK: {
Boolean fileDeleted = FALSE;
Fsio_FileIOHandle *deletedHandlePtr;
/*
* The presence of FS_LINK means that curHandlePtr references
* a file that is being linked to. If the file already exists
* it is deleted first. Then link is made with LinkFile.
*/
if (useFlags & FS_RENAME) {
logOp = FSDM_LOG_RENAME_LINK;
fs_Stats.lookup.forRename++;
} else {
logOp = FSDM_LOG_LINK;
fs_Stats.lookup.forLink++;
}
dirFileNumber = parentHandlePtr->hdr.fileID.minor;
if (status == SUCCESS) {
/*
* Linking to an existing file.
* This can only be in preparation for a rename.
*/
if ((useFlags & FS_RENAME) &&
(curHandlePtr->descPtr->fileType == type)) {
/*
* Make sure the two files aren't actually the
* same. If they are then just return SUCCESS,
* otherwise we would deadlock when we try to lock
* both file handles, unaware that they are the
* same handle.
*/
if (fileNumber == curHandlePtr->hdr.fileID.minor) {
break;
}
/*
* Try the delete, this fails on non-empty directories.
*/
deletedHandlePtr = curHandlePtr;
status = DeleteFileName(parentHandlePtr,
curHandlePtr, component, compLen, FALSE, idPtr,
FSDM_LOG_RENAME_DELETE, clientID);
if (status == SUCCESS) {
fileDeleted = TRUE;
}
} else {
/*
* Not ok to link to an existing file.
*/
status = FS_FILE_EXISTS;
}
} else if (status == FS_FILE_NOT_FOUND) {
/*
* The file does not already exist. Check write permission
* in the parent before making the link.
*/
status = CheckPermissions(parentHandlePtr, FS_WRITE, idPtr,
FS_DIRECTORY);
}
if (status == SUCCESS) {
status = LinkFile(parentHandlePtr,
component, compLen, fileNumber, logOp,
&curHandlePtr, clientID);
if (status == SUCCESS) {
(void)Fsdm_FileDescStore(curHandlePtr, FALSE);
}
}
if (fileDeleted) {
CloseDeletedFile(&parentHandlePtr, &deletedHandlePtr);
}
break;
}
case FS_DELETE:
fs_Stats.lookup.forDelete++;
if (status == SUCCESS) {
if ((curHandlePtr->descPtr->fileType != type) &&
(type != FS_FILE)) {
status = (type == FS_DIRECTORY) ? FS_NOT_DIRECTORY :
FS_WRONG_TYPE;
} else {
logOp = (useFlags&FS_RENAME) ? FSDM_LOG_RENAME_UNLINK :
FSDM_LOG_UNLINK;
status = DeleteFileName(parentHandlePtr, curHandlePtr,
component, compLen,
(int) (useFlags & FS_RENAME), idPtr, logOp,
clientID);
if (status == SUCCESS) {
CloseDeletedFile(&parentHandlePtr,
&curHandlePtr);
}
}
}
break;
}
}
/*
* Clean up state and return a fileHandle to our caller.
*/
if (newNameBuffer != (char *)NIL) {
free(newNameBuffer);
}
if (parentHandlePtr != (Fsio_FileIOHandle *)NIL) {
Fsutil_HandleRelease(parentHandlePtr, TRUE);
}
if (curHandlePtr != (Fsio_FileIOHandle *)NIL) {
if (status != SUCCESS) {
Fsutil_HandleRelease(curHandlePtr, TRUE);
curHandlePtr = (Fsio_FileIOHandle *)NIL;
}
}
if (handlePtrPtr != (Fsio_FileIOHandle **)NIL) {
/*
* Return a locked handle that has had its reference count bumped.
*/
*handlePtrPtr = curHandlePtr;
} else if (curHandlePtr != (Fsio_FileIOHandle *)NIL) {
printf( "FslclLookup: caller didn't want handle\n");
Fsutil_HandleRelease(curHandlePtr, TRUE);
}
if ((status != SUCCESS) ||
(handlePtrPtr == (Fsio_FileIOHandle **)NIL)) {
Fsdm_DomainRelease(prefixHdrPtr->fileID.major);
}
if (status == FS_FILE_NOT_FOUND) {
fs_Stats.lookup.notFound++;
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FindComponent --
*
* Look for a pathname component in a directory. This returns a locked
* file handle that has its reference count incremented.
*
* Results:
* SUCCESS or FS_FILE_NOT_FOUND
*
* Side effects:
* Disk I/O, installing and locking the handle.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
FindComponent(parentHandlePtr, component, compLen, isDotDot, curHandlePtrPtr,
dirOffsetPtr)
Fsio_FileIOHandle *parentHandlePtr; /* Locked handle of current
* directory */
register char *component; /* Name of path component to
* find */
register int compLen; /* The number of characters in
* component */
Boolean isDotDot; /* TRUE if component is "..".
* In this case the handle for
* the parent is returned
* UNLOCKED. */
Fsio_FileIOHandle **curHandlePtrPtr; /* Return, locked handle */
int *dirOffsetPtr; /* OUT: Offset into directory
* off component. */
{
register Fslcl_DirEntry *dirEntryPtr; /* Reference to directory entry */
register char *s1; /* Pointers into components used */
register char *s2; /* for fast in-line string compare */
register int blockOffset; /* Offset within the directory */
ReturnStatus status;
Fscache_Block *cacheBlockPtr; /* Cache block */
int dirBlockNum; /* Block number within directory */
int length; /* Length variable for read call */
FslclHashEntry *entryPtr; /* Name cache entry */
Fs_FileID fileID; /* Used when fetching handles */
/*
* Check in system-wide name cache here before scanning
* the directory's data blocks.
*/
entryPtr = FSLCL_HASH_LOOK_ONLY(fslclNameTablePtr, component, parentHandlePtr);
if (entryPtr != (FslclHashEntry *)NIL) {
if (entryPtr->hdrPtr->fileID.type != FSIO_LCL_FILE_STREAM) {
panic(
"FindComponent: got trashy handle from cache");
} else {
if (isDotDot) {
/*
* Unlock this directory before grabbing the handle for "..".
* This prevents deadlock with another lookup that is
* descending from our parent ("..") into this directory.
*/
Fsutil_HandleUnlock(parentHandlePtr);
}
*curHandlePtrPtr = Fsutil_HandleDupType(Fsio_FileIOHandle,
entryPtr->hdrPtr);
return(SUCCESS);
}
}
dirBlockNum = 0;
do {
status = Fscache_BlockRead(&parentHandlePtr->cacheInfo, dirBlockNum,
&cacheBlockPtr, &length, FSCACHE_DIR_BLOCK, FALSE);
if (status != SUCCESS || length == 0) {
*curHandlePtrPtr = (Fsio_FileIOHandle *)NIL;
return(FS_FILE_NOT_FOUND);
}
dirEntryPtr = (Fslcl_DirEntry *)cacheBlockPtr->blockAddr;
blockOffset = 0;
while (blockOffset < length) {
if (dirEntryPtr->recordLength <= 0) {
printf("Corrupted directory?");
printf(" File ID <%d, %d, %d>",
parentHandlePtr->hdr.fileID.serverID,
parentHandlePtr->hdr.fileID.major,
parentHandlePtr->hdr.fileID.minor);
printf(" dirBlockNum <%d>, blockOffset <%d>",
dirBlockNum, blockOffset);
printf("\n");
Fscache_UnlockBlock(cacheBlockPtr, (time_t) 0,
-1, 0, FSCACHE_CLEAR_READ_AHEAD);
*curHandlePtrPtr = (Fsio_FileIOHandle *)NIL;
return(FS_FILE_NOT_FOUND);
}
if (dirEntryPtr->fileNumber != 0) {
/*
* A valid directory record. If component and the directory
* entry are the same length then compare them for a match.
* This String Compare is in-lined for efficiency.
*/
if ((dirEntryPtr->nameLength == compLen)) {
s1 = component;
s2 = dirEntryPtr->fileName;
do {
if (*s1 == '\0') {
/*
* The strings are the same length so hitting
* the end of component indicates a match.
*/
if (isDotDot) {
/*
* Unlock this directory before grabbing the
* handle for "..". This prevents deadlock
* with another lookup that is descending
* from our parent ("..") into this directory.
*/
Fsutil_HandleUnlock(parentHandlePtr);
}
*dirOffsetPtr = blockOffset +
dirBlockNum * FS_BLOCK_SIZE;
/*
* Inlined call to GetHandle().
*/
fileID.type = FSIO_LCL_FILE_STREAM;
fileID.serverID = rpc_SpriteID;
fileID.major = parentHandlePtr->hdr.fileID.major;
fileID.minor = dirEntryPtr->fileNumber;
status = Fsio_LocalFileHandleInit(&fileID, component,
(Fsdm_FileDescriptor *) NIL, FALSE,
curHandlePtrPtr);
Fscache_UnlockBlock(cacheBlockPtr, (time_t) 0,
-1, 0, FSCACHE_CLEAR_READ_AHEAD);
if (status == SUCCESS) {
FSLCL_HASH_INSERT(fslclNameTablePtr, component,
parentHandlePtr, *curHandlePtrPtr);
} else {
/*
* It is possible that the file doesn't
* exist anymore. This happens when
* the parent directory of an open
* directory is deleted. The ".." entry
* points to a deleted file.
*/
if (status == FS_FILE_REMOVED) {
status = FS_FILE_NOT_FOUND;
} else {
printf(
"FindComponent, no handle <0x%x> for \"%s\" fileNumber %d\n",
status, component, dirEntryPtr->fileNumber);
}
if (isDotDot) {
/*
* If we have an error, relock the handle
* because your caller will assume that it
* is locked.
*/
Fsutil_HandleLock(parentHandlePtr);
}
}
goto exit; /* to quiet lint... */
#ifndef lint
} else if (*s1++ != *s2++) {
break;
#else
} else {
if (*s1 != *s2) {
break;
}
s1++;
s2++;
#endif
}
} while (TRUE);
}
}
blockOffset += dirEntryPtr->recordLength;
dirEntryPtr = (Fslcl_DirEntry *)((int)dirEntryPtr +
dirEntryPtr->recordLength);
}
dirBlockNum++;
Fscache_UnlockBlock(cacheBlockPtr, (time_t) 0, -1, 0, 0);
} while(TRUE);
exit:
return(status);
}
/*
*----------------------------------------------------------------------
*
* InsertComponent --
* Add a name to a directory.
*
* Results:
* SUCCESS or an error code.
*
* Side effects:
* Adding the name to the directory, writing the modified directory block.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
InsertComponent(curHandlePtr, component, compLen, fileNumber, dirOffsetPtr)
Fsio_FileIOHandle *curHandlePtr; /* Locked handle of current directory */
char *component; /* Name of path component to insert */
int compLen; /* The length of component */
int fileNumber; /* File Number of inserted name */
int *dirOffsetPtr; /* OUT: directory offset of component's entry.*/
{
ReturnStatus status;
int dirBlockNum; /* Directory block index */
int blockOffset; /* Offset within a directory block. */
Fslcl_DirEntry *dirEntryPtr; /* Reference to directory entry. */
int length; /* Length variable for read call. */
int recordLength; /* Length of directory entry for
* component. */
int freeSpace; /* Total amount of free bytes in a
* directory block. */
int extraBytes; /* The number of free bytes attached to
* a directory entry. */
Fscache_Block *cacheBlockPtr; /* Cache block. */
length = FS_BLOCK_SIZE;
recordLength = Fslcl_DirRecLength(compLen);
/*
* Loop through the directory blocks looking for space of at least
* recordLength in which to insert the new directory record.
*/
dirBlockNum = 0;
do {
/*
* Read in a full data block.
*/
status = Fscache_BlockRead(&curHandlePtr->cacheInfo, dirBlockNum,
&cacheBlockPtr, &length, FSCACHE_DIR_BLOCK, TRUE);
if (status != SUCCESS) {
printf( "InsertComponent: Read failed\n");
return(status);
} else if (length == 0) {
/*
* No more space, have to grow the directory. First we
* need another cache block.
*/
Boolean found = FALSE;
Fscache_FetchBlock(&curHandlePtr->cacheInfo, dirBlockNum,
FSCACHE_DIR_BLOCK, &cacheBlockPtr, &found);
if (found) {
panic( "InsertComponent found new dir block");
}
bzero(cacheBlockPtr->blockAddr, FS_BLOCK_SIZE);
}
dirEntryPtr = (Fslcl_DirEntry *)cacheBlockPtr->blockAddr;
blockOffset = 0;
freeSpace = 0;
while (blockOffset < length) {
/*
* Scan a data block looking for deleted entries that are
* large enough to use, or for entries that contain enough extra
* bytes for the record we have to insert. The amount of fragmented
* space is accumulated in freeSpace but compaction is not
* implemented.
*/
if (dirEntryPtr->fileNumber != 0) {
/*
* A valid directory record.
* Check the left-over bytes attached to this record.
*/
extraBytes = dirEntryPtr->recordLength -
Fslcl_DirRecLength(dirEntryPtr->nameLength);
if (extraBytes >= recordLength) {
/*
* Can fit new entry in the space left over.
*/
goto haveASlot;
}
/*
* Count bytes that occur in fragments of 4 bytes or more.
*/
freeSpace += extraBytes & ~(FSLCL_REC_LEN_GRAIN-1);
} else {
/*
* A deleted name in the directory.
*/
if (dirEntryPtr->recordLength >= recordLength) {
goto haveASlot;
}
freeSpace += dirEntryPtr->recordLength;
}
blockOffset += dirEntryPtr->recordLength;
dirEntryPtr = (Fslcl_DirEntry *)((int)dirEntryPtr +
dirEntryPtr->recordLength);
}
/*
* Finished scanning a directory block. Note if the accumulated
* free bytes in the block could be used for a directory entry.
* (We aren't implementing compaction for a while yet...)
*/
if (freeSpace >= recordLength) {
fsCompacts++;
}
if (length < FS_BLOCK_SIZE) {
/*
* Scanned up to the end of the last directory data block. Need
* to append to the end of the directory.
*/
bzero(cacheBlockPtr->blockAddr + length, FSLCL_DIR_BLOCK_SIZE);
dirEntryPtr->recordLength = FSLCL_DIR_BLOCK_SIZE;
length += FSLCL_DIR_BLOCK_SIZE;
break;
}
dirBlockNum++;
Fscache_UnlockBlock(cacheBlockPtr,
(time_t) 0, -1, 0, FSCACHE_CLEAR_READ_AHEAD);
} while(TRUE);
haveASlot:
/*
* At this point dirEntryPtr references the slot we have to either re-use
* or that has enough free bytes for us to use.
*/
if (dirEntryPtr->fileNumber != 0) {
/*
* Have to take space away from the end of a valid directory entry.
*/
int newRecordLength; /* New length of the existing valid entry */
Fslcl_DirEntry *tmpDirEntryPtr; /* Pointer to new slot */
newRecordLength = Fslcl_DirRecLength(dirEntryPtr->nameLength);
tmpDirEntryPtr = (Fslcl_DirEntry *)((int)dirEntryPtr + newRecordLength);
tmpDirEntryPtr->recordLength = dirEntryPtr->recordLength -
newRecordLength;
dirEntryPtr->recordLength = newRecordLength;
dirEntryPtr = tmpDirEntryPtr;
}
dirEntryPtr->fileNumber = fileNumber;
dirEntryPtr->nameLength = compLen;
(void)strcpy(dirEntryPtr->fileName, component);
blockOffset = (((char *)dirEntryPtr) - (char *)(cacheBlockPtr->blockAddr));
*dirOffsetPtr = dirBlockNum * FS_BLOCK_SIZE + blockOffset;
status = CacheDirBlockWrite(curHandlePtr,cacheBlockPtr,dirBlockNum,length);
return(status);
}
/*
*----------------------------------------------------------------------
*
* DeleteComponent --
*
* Delete a name from a directory. The file descriptor itself
* is not modified. It is assumed that the handle for the file that
* is being removed is already locked.
*
* Results:
* SUCCESS or FS_FILE_NOT_FOUND
*
* Side effects:
* Sets the fileNumber for the component to Zero (0) to mark as deleted.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
DeleteComponent(parentHandlePtr, component, compLen, dirOffsetPtr)
Fsio_FileIOHandle *parentHandlePtr;/* Locked handle of current dir. */
char *component; /* Name to delete */
int compLen; /* Length of the name */
int *dirOffsetPtr; /* OUT: Directory offset of component.*/
{
ReturnStatus status;
int blockOffset; /* Offset within a directory block */
Fslcl_DirEntry *dirEntryPtr; /* Reference to directory entry */
Fslcl_DirEntry *lastDirEntryPtr;/* Back pointer used when merging
* adjacent entries after the delete */
int length; /* Length variable for read call */
Fscache_Block *cacheBlockPtr; /* Cache block. */
int dirBlockNum;
dirBlockNum = 0;
do {
status = Fscache_BlockRead(&parentHandlePtr->cacheInfo, dirBlockNum,
&cacheBlockPtr, &length, FSCACHE_DIR_BLOCK, FALSE);
if (status != SUCCESS || length == 0) {
return(FS_FILE_NOT_FOUND);
}
blockOffset = 0;
lastDirEntryPtr = (Fslcl_DirEntry *)NIL;
dirEntryPtr = (Fslcl_DirEntry *)cacheBlockPtr->blockAddr;
while (blockOffset < length) {
if ((dirEntryPtr->fileNumber != 0) &&
(dirEntryPtr->nameLength == compLen) &&
(strcmp(component, dirEntryPtr->fileName) == 0)) {
/*
* Delete the entry from the name cache.
*/
*dirOffsetPtr = blockOffset + FS_BLOCK_SIZE*dirBlockNum;
FSLCL_HASH_DELETE(fslclNameTablePtr, component,parentHandlePtr);
dirEntryPtr->fileNumber = 0;
if (lastDirEntryPtr != (Fslcl_DirEntry *)NIL) {
/*
* Grow the previous record so that it now includes
* this one.
*/
lastDirEntryPtr->recordLength += dirEntryPtr->recordLength;
}
/*
* Write out the modified directory block.
*/
status = CacheDirBlockWrite(parentHandlePtr, cacheBlockPtr,
dirBlockNum, length);
return(status);
}
blockOffset += dirEntryPtr->recordLength;
if ((blockOffset & (FSLCL_DIR_BLOCK_SIZE - 1)) == 0) {
lastDirEntryPtr = (Fslcl_DirEntry *) NIL;
} else {
lastDirEntryPtr = dirEntryPtr;
}
dirEntryPtr =
(Fslcl_DirEntry *)((int)dirEntryPtr + dirEntryPtr->recordLength);
}
dirBlockNum++;
Fscache_UnlockBlock(cacheBlockPtr, (time_t) 0,
-1, 0, FSCACHE_CLEAR_READ_AHEAD);
} while(TRUE);
/*NOTREACHED*/
}
/*
*----------------------------------------------------------------------
*
* ExpandLink --
* Expand a link by shifting the remaining pathname over and
* inserting the contents of the link file.
*
* Results:
* None.
*
* Side effects:
* Expands the link into nameBuffer.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
ExpandLink(curHandlePtr, curCharPtr, offset, nameBuffer)
Fsio_FileIOHandle *curHandlePtr; /* Handle on the link file */
char *curCharPtr; /* Points to beginning of the remaining
* name that has to be shifted */
int offset; /* Offset of curCharPtr within its
* buffer */
char nameBuffer[]; /* New buffer for the expanded name */
{
ReturnStatus status;
int linkNameLength; /* The length of the name in the
* link NOT including the Null */
register char *srcPtr;
register char *dstPtr;
linkNameLength = curHandlePtr->descPtr->lastByte; /* + 1 */
if (linkNameLength < 0) { /* <= */
return(FS_FILE_NOT_FOUND);
}
if (*curCharPtr == '\0') {
/*
* There is no pathname, just make sure the new name is Null terminated
*/
nameBuffer[linkNameLength] = '\0'; /* still ok */
} else {
/*
* Set up srcPtr to point to the start of the remaining pathname.
* Set up dstPtr to point to just after where the link will be,
* ie. just where the '/' is that separates the link value from
* the remaining pathname.
*/
srcPtr = curCharPtr;
dstPtr = &nameBuffer[linkNameLength];
if (linkNameLength < offset) {
/*
* The remaining name has to be shifted left.
* For example, /first is a link to /usr (or /users). With the
* filename /first/abc, curCharPtr will point to the 'a' in "abc".
* dstPtr points to the '\0' after "/usr".
*
* / f i r s t / a b c \0 { current name, offset = 7 }
* / u s r \0 { link name, len = 4 }
* / u s e r s \0 { or len = 6 }
* / u s r / a b c { final result }
*
* Insert the separating '/' first, then start at the beginning
* of the remaining name and copy it into the new buffer.
*/
*dstPtr = '/';
dstPtr++;
for( ; ; ) {
*dstPtr = *srcPtr;
if (*srcPtr == '\0') {
break;
}
dstPtr++;
srcPtr++;
}
} else {
/*
* The remaining name has to be shifted right.
* For example, /first is a link to /usr/tmp. With the filename
* /first/abc, curCharPtr will point to the 'a' in "abc".
* dstPtr points to the '\0' after "/usr/tmp".
*
* / f i r s t / a b c \0 { current name, offset = 7 }
* / u s r / t m p \0 { link name, len = 8 }
* / u s r / t m p / a b c { final result }
*
* Zoom to the end of the name (adjusting dstPtr to account
* for where the '/' will go) and then shift the name right.
*/
dstPtr++;
while (*srcPtr != '\0') {
srcPtr++;
dstPtr++;
}
while (srcPtr >= curCharPtr) {
*dstPtr = *srcPtr;
dstPtr--;
srcPtr--;
}
*dstPtr = '/';
}
}
/*
* Read and insert the link name in front of the remaining name
* that was just shifted over.
*/
status = Fscache_Read(&curHandlePtr->cacheInfo, 0, nameBuffer, 0,
&linkNameLength, (Sync_RemoteWaiter *)NIL);
if ((status == SUCCESS) || (linkNameLength > 0)) {
(void)Fsdm_UpdateDescAttr(curHandlePtr, &curHandlePtr->cacheInfo.attr,
FSDM_FD_ACCESSTIME_DIRTY);
}
/*
* FIX HERE to handle old sprite links that include a null.
*/
#ifdef notdef
if (nameBuffer[linkNameLength-1] == '\0') {
/*
* Old Sprite link with trailing NULL. Shift remaining name over
* to the left one place.
*/
dstPtr = &nameBuffer[linkNameLength-1];
srcPtr = dstPtr + 1;
while (*srcPtr != '\0') {
*dstPtr++ = *srcPtr++;
}
*dstPtr = '\0'
}
#endif /* notdef */
return status;
}
/*
*----------------------------------------------------------------------
*
* GetHandle --
*
* Given a file number and the handle on the parent directotry,
* this routine returns a locked handle for the file. This is a
* small layer on top of Fsio_LocalFileHandleInit that is oriented
* towards the needs of the lookup routines.
*
* Results:
* A return code. If SUCCESS the returned handle is locked.
*
* Side effects:
* Calls Fsio_LocalFileHandleInit to set up the handle.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
GetHandle(fileNumber, newDescPtr, curHandlePtr, name, newHandlePtrPtr)
int fileNumber; /* Number of file to get handle for */
Fsdm_FileDescriptor *newDescPtr;
Fsio_FileIOHandle *curHandlePtr; /* Handle on file in the same domain */
char *name; /* File name for error msgs */
Fsio_FileIOHandle **newHandlePtrPtr;/* Return, ref. to installed handle */
{
register ReturnStatus status;
Fs_FileID fileID;
fileID.type = FSIO_LCL_FILE_STREAM;
fileID.serverID = rpc_SpriteID;
fileID.major = curHandlePtr->hdr.fileID.major;
fileID.minor = fileNumber;
status = Fsio_LocalFileHandleInit(&fileID, name, newDescPtr, FALSE,
newHandlePtrPtr);
return(status);
}
/*
*----------------------------------------------------------------------
*
* CreateFile --
* Create a file by adding it do a directory and and initializing its
* file descriptor. The caller has already allocated the fileNumber
* for the file. The file descriptor is written to disk before the
* name is inserted into the directory.
*
* Results:
* SUCCESS or an error code.
*
* Side effects:
* Calls InsertComponent, Fsdm_FileDescInit.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
CreateFile(domainPtr, parentHandlePtr, component, compLen, fileNumber, type,
permissions, idPtr, curHandlePtrPtr, dirOffsetPtr)
Fsdm_Domain *domainPtr; /* Domain of the file */
Fsio_FileIOHandle *parentHandlePtr;/* Handle of directory in which to add
* file. */
char *component; /* Name of the file */
int compLen; /* The length of component */
int fileNumber; /* Domain relative file number */
int type; /* Type of the file */
int permissions; /* Permission bits on the file */
Fs_UserIDs *idPtr; /* User ID of calling process */
Fsio_FileIOHandle **curHandlePtrPtr;/* Return, handle for the new file */
int *dirOffsetPtr; /* OUT: directory offset of component's entry.*/
{
ReturnStatus status;
Fsdm_FileDescriptor *parentDescPtr; /* Descriptor for the parent */
Fsdm_FileDescriptor *newDescPtr; /* Descriptor for the new file */
/*
* Set up the file descriptor using the group ID from the parent directory.
*/
parentDescPtr = parentHandlePtr->descPtr;
newDescPtr = (Fsdm_FileDescriptor *)malloc(sizeof(Fsdm_FileDescriptor));
status = Fsdm_FileDescInit(domainPtr, fileNumber, type, permissions,
idPtr->user, parentDescPtr->gid, newDescPtr);
if (status == SUCCESS) {
if (type == FS_DIRECTORY) {
/*
* Both the parent and the directory itself reference it.
*/
newDescPtr->numLinks = 2;
newDescPtr->flags |= FSDM_FD_LINKS_DIRTY;
}
/*
* GetHandle does extra work because we already have the
* file descriptor all set up...
*/
status = GetHandle(fileNumber, newDescPtr, parentHandlePtr, component,
curHandlePtrPtr);
if (status != SUCCESS) {
/*
* GetHandle shouldn't fail because we just wrote out
* the file descriptor.
*/
panic( "CreateFile: GetHandle failed\n");
} else {
if (type == FS_DIRECTORY) {
status = WriteNewDirectory(*curHandlePtrPtr,
parentHandlePtr);
}
if (status == SUCCESS) {
/*
* Commit by adding the name to the directory. If we crash
* after this the file will show up and have a good
* descriptor for itself on disk.
*/
status = InsertComponent(parentHandlePtr, component,
compLen, fileNumber, dirOffsetPtr);
if (type == FS_DIRECTORY) {
if (status == SUCCESS) {
/*
* Update the parent directory to reflect
* the addition of a new sub-directory.
*/
parentDescPtr->numLinks++;
parentDescPtr->descModifyTime = Fsutil_TimeInSeconds();
parentDescPtr->flags |= FSDM_FD_LINKS_DIRTY;
(void)Fsdm_FileDescStore(parentHandlePtr, FALSE);
}
}
}
if (status != SUCCESS) {
/*
* Couldn't add to the directory, no disk space perhaps.
* Unwind by marking the file descriptor as free and
* releasing the handle we've created.
*/
panic("CreateFile: aborting create of %d (%s) in %d\n",
fileNumber, component,
parentHandlePtr->hdr.fileID.minor);
newDescPtr->flags = FSDM_FD_FREE;
Fsutil_HandleRelease(*curHandlePtrPtr, TRUE);
*curHandlePtrPtr = (Fsio_FileIOHandle *)NIL;
}
}
}
if (status != SUCCESS) {
free((Address) newDescPtr);
} else {
(void)Fsdm_FileDescStore(parentHandlePtr, FALSE);
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* WriteNewDirectory --
* Write a new sub-directory. Set the file numbers on the canned image
* of a new directory and write the block to the directory.
*
* Results:
* SUCCESS or an error code.
*
* Side effects:
* Disk I/O to write the directory data block.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
WriteNewDirectory(curHandlePtr, parentHandlePtr)
Fsio_FileIOHandle *curHandlePtr; /* Handle of file to delete */
Fsio_FileIOHandle *parentHandlePtr; /* Handle of directory in which
* to delete file*/
{
ReturnStatus status;
int offset;
int length;
register Fslcl_DirEntry *dirEntryPtr;
char *dirBlock;
/*
* The malloc and Byte_Copy could be avoided by puting this routine
* into its own monitor so that it could write directly onto fslclEmptyDirBlock
* fslclEmptyDirBlock is already set up with ".", "..", and the correct
* nameLengths and recordLengths.
*/
dirBlock = (char *)malloc(FSLCL_DIR_BLOCK_SIZE);
bcopy((Address)fslclEmptyDirBlock, (Address)dirBlock, FSLCL_DIR_BLOCK_SIZE);
dirEntryPtr = (Fslcl_DirEntry *)dirBlock;
dirEntryPtr->fileNumber = curHandlePtr->hdr.fileID.minor;
dirEntryPtr = (Fslcl_DirEntry *)((int)dirEntryPtr +
dirEntryPtr->recordLength);
dirEntryPtr->fileNumber = parentHandlePtr->hdr.fileID.minor;
offset = 0;
length = FSLCL_DIR_BLOCK_SIZE;
status = Fscache_Write(&curHandlePtr->cacheInfo, 0, (Address)dirBlock,
offset, &length, (Sync_RemoteWaiter *)NIL);
if (status == SUCCESS) {
(void)Fsdm_UpdateDescAttr(curHandlePtr, &curHandlePtr->cacheInfo.attr,
FSDM_FD_MODTIME_DIRTY);
}
free(dirBlock);
return(status);
}
/*
*----------------------------------------------------------------------
*
* LinkFile --
* Create an entry in a directory that references an existing file.
* This understands that links to directories are made as part of
* a rename and checks that no circularities in the directory structure
* result from moving a directory. Other than that it is left to
* the caller to check permissions.
*
* Results:
* SUCCESS or an error code.
*
* Side effects:
* Calls InsertComponent, bumps the link count on the existing file.
* Calls MoveDirectory in the case of directories.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
LinkFile(parentHandlePtr, component, compLen, fileNumber, logOp,curHandlePtrPtr,
clientID)
Fsio_FileIOHandle *parentHandlePtr;/* Handle of directory in which to add
* file. */
char *component; /* Name of the file */
int compLen; /* The length of component */
int fileNumber; /* Domain relative file number */
int logOp;
Fsio_FileIOHandle **curHandlePtrPtr;/* Return, handle for the new file */
int clientID; /* ID of requesting client. */
{
ReturnStatus status;
Fsdm_FileDescriptor *linkDescPtr; /* Descriptor for the existing file */
Time modTime; /* Descriptors are modified */
ClientData logClientData;
ClientData recovLogClientData = (ClientData) 0;
int dirOffset;
if (fileNumber == parentHandlePtr->hdr.fileID.minor) {
/*
* Trying to move a directory into itself, ie. % mv subdir subdir
*/
return(GEN_INVALID_ARG);
}
status = GetHandle(fileNumber, (Fsdm_FileDescriptor *) NIL,
parentHandlePtr, component, curHandlePtrPtr);
if (status != SUCCESS) {
printf( "LinkFile: can't get existing file handle\n");
return(FAILURE);
} else if ((*curHandlePtrPtr)->descPtr->fileType == FS_DIRECTORY) {
logOp |= FSDM_LOG_IS_DIRECTORY;
status = OkToMoveDirectory(parentHandlePtr, *curHandlePtrPtr);
}
if (status == SUCCESS) {
linkDescPtr = (*curHandlePtrPtr)->descPtr;
logClientData = Fsdm_DirOpStart(logOp, parentHandlePtr, -1,
component, compLen, fileNumber,
linkDescPtr->fileType,
linkDescPtr);
if (recov_Transparent && clientID != rpc_SpriteID) {
recovLogClientData = Fsrecov_DirOpStart(logOp, parentHandlePtr, -1,
component, compLen, fileNumber,
linkDescPtr->fileType, linkDescPtr);
}
linkDescPtr->numLinks++;
linkDescPtr->descModifyTime = Fsutil_TimeInSeconds();
linkDescPtr->flags |= FSDM_FD_LINKS_DIRTY;
status = Fsdm_FileDescStore(*curHandlePtrPtr, FALSE);
if (status == SUCCESS) {
/*
* Commit by adding the name to the directory.
*/
status = InsertComponent(parentHandlePtr, component, compLen,
fileNumber, &dirOffset);
if ((*curHandlePtrPtr)->descPtr->fileType == FS_DIRECTORY &&
status == SUCCESS) {
/*
* This link is part of a rename (links to directories are
* only allowed at this time), and the ".." entry in the
* directory may have to be fixed.
*/
modTime.seconds = Fsutil_TimeInSeconds();
status = MoveDirectory(&modTime, parentHandlePtr,
*curHandlePtrPtr);
if (status != SUCCESS) {
(void)DeleteComponent(parentHandlePtr, component,
compLen, &dirOffset);
}
}
if (status != SUCCESS) {
/*
* Couldn't add to the directory because of I/O probably.
* Unwind by reducing the link count. This gets the cache
* consistent with the cached directory image anyway.
*/
linkDescPtr->numLinks--;
linkDescPtr->flags |= FSDM_FD_LINKS_DIRTY;
(void)Fsdm_FileDescStore(*curHandlePtrPtr, FALSE);
Fsutil_HandleRelease(*curHandlePtrPtr, TRUE);
*curHandlePtrPtr = (Fsio_FileIOHandle *)NIL;
} else {
(void)Fsdm_FileDescStore(parentHandlePtr, FALSE);
}
} else {
linkDescPtr->numLinks--;
linkDescPtr->flags &= ~FSDM_FD_LINKS_DIRTY;
}
Fsdm_DirOpEnd(logOp, parentHandlePtr, dirOffset,
component, compLen, fileNumber, linkDescPtr->fileType,
linkDescPtr, logClientData, status);
if (recov_Transparent && clientID != rpc_SpriteID) {
Fsrecov_DirOpEnd(logOp, parentHandlePtr, dirOffset,
component, compLen, fileNumber, linkDescPtr->fileType,
linkDescPtr, recovLogClientData, status);
}
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* OkToMoveDirectory --
*
* Check that it is ok to move a directory. Dis-connected trees and
* loops in directory structure are prevented here.
*
* Results:
* A return code.
*
* Side effects:
* File handles are momentarily locked on the route from the new
* position in the hierarchy up to the root to check against illegal moves.
* This action might cause deadlock with another process desending along
* same route, descenders hold a lock on the parent while they try for
* a lock on the next sub-directory.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
OkToMoveDirectory(newParentHandlePtr, curHandlePtr)
Fsio_FileIOHandle *newParentHandlePtr; /* New parent directory for
* curHandlePtr */
Fsio_FileIOHandle *curHandlePtr; /* Directory being moved */
{
ReturnStatus status;
int oldParentFileNumber; /* File number of original
* parent directory. */
int newParentNumber; /* File number of new
* parent directory. */
status = GetParentNumber(curHandlePtr, &oldParentFileNumber);
if (status != SUCCESS) {
return(status);
}
newParentNumber = newParentHandlePtr->hdr.fileID.minor;
if (oldParentFileNumber == newParentNumber) {
/*
* ".." entry is ok because the rename is within the same directory.
*/
status = SUCCESS;
} else {
/*
* Have to trace up from the new parent to make sure we don't find
* the current file. If we let that happen then you create
* dis-connected loops in the directory structure.
*/
Fsio_FileIOHandle *parentHandlePtr;
int parentNumber;
for (parentNumber = newParentNumber; status == SUCCESS; ) {
if (parentNumber == curHandlePtr->hdr.fileID.minor) {
status = FS_INVALID_ARG;
} else if (parentNumber == FSDM_ROOT_FILE_NUMBER ||
parentNumber == oldParentFileNumber) {
break;
} else {
/*
* Advance parentNumber upwards towards the root. The
* handles are locked because of the I/O required to get
* the parent file number. We have to be careful when
* locking the parents because the first parent
* (newParentHandlePtr) is already locked by us.
*
* It still seems like there is a possibility of deadlock if
* someone is desending into newParentHandlePtr and has a lock
* on the directory above that. FIX ME
*/
if (parentNumber != newParentNumber) {
status = GetHandle(parentNumber,
(Fsdm_FileDescriptor *) NIL, curHandlePtr, (char *)NIL,
&parentHandlePtr);
} else {
parentHandlePtr = newParentHandlePtr;
}
if (status == SUCCESS) {
status = GetParentNumber(parentHandlePtr, &parentNumber);
}
if (parentHandlePtr != newParentHandlePtr) {
(void)Fsutil_HandleRelease(parentHandlePtr, TRUE);
}
}
}
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* MoveDirectory --
*
* Complete the moving of a directory by updating its reference to
* its parent and incrementing the link count on its new parent.
*
* Results:
* A return code.
*
* Side effects:
* Sets the directory's parent entry to reference the parent passed in.
* Increments the link count on the new parent.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
MoveDirectory( modTimePtr, newParentHandlePtr, curHandlePtr)
Time *modTimePtr; /* Modify time for parent */
Fsio_FileIOHandle *newParentHandlePtr; /* New parent directory for
* curHandlePtr */
Fsio_FileIOHandle *curHandlePtr; /* Directory being moved */
{
ReturnStatus status;
int oldParentFileNumber; /* File number of original
* parent directory */
int newParentNumber; /* File number of new
* parent directory */
status = GetParentNumber(curHandlePtr, &oldParentFileNumber);
if (status != SUCCESS) {
printf(
"MoveDirectory: Can't get parent's file number\n");
} else {
newParentNumber = newParentHandlePtr->hdr.fileID.minor;
if (oldParentFileNumber != newParentNumber) {
/*
* Patch the directory entry for ".."
*/
FSLCL_HASH_DELETE(fslclNameTablePtr, "..", curHandlePtr);
status = SetParentNumber(curHandlePtr, newParentNumber);
}
if (status == SUCCESS) {
/*
* Increment the link count on the new parent. The old parent's
* link count gets decremented when the orignial instance of the
* directory is removed. This is done even if the move is within
* the same directory because deleting the original name later will
* reduce the link count.
*/
register Fsdm_FileDescriptor *parentDescPtr;
parentDescPtr = newParentHandlePtr->descPtr;
parentDescPtr->numLinks++;
parentDescPtr->descModifyTime = modTimePtr->seconds;
parentDescPtr->flags |= FSDM_FD_LINKS_DIRTY;
(void)Fsdm_FileDescStore(newParentHandlePtr, FALSE);
}
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* GetParentNumber --
* Read a directory to determine the file number of the parent.
*
* Results:
* SUCCESS or an error code, sets *parentNumberPtr to be the file number
* from the ".." entry in the directory.
*
* Side effects:
* None, except for the I/O to read the directory block.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
GetParentNumber(curHandlePtr, parentNumberPtr)
Fsio_FileIOHandle *curHandlePtr; /* Handle for current directory */
int *parentNumberPtr; /* Result, the file number of the parent
* of curHandlePtr */
{
ReturnStatus status;
int length;
register Fslcl_DirEntry *dirEntryPtr;
Fscache_Block *cacheBlockPtr;
status = Fscache_BlockRead(&curHandlePtr->cacheInfo, 0, &cacheBlockPtr,
&length, FSCACHE_DIR_BLOCK, FALSE);
if (status != SUCCESS) {
return(status);
} else if (length == 0) {
return(FAILURE);
}
dirEntryPtr = (Fslcl_DirEntry *)cacheBlockPtr->blockAddr;
if (dirEntryPtr->nameLength != 1 ||
dirEntryPtr->fileName[0] != '.' ||
dirEntryPtr->fileName[1] != '\0') {
printf(
"GetParentNumber: \".\", corrupted directory\n");
status = FAILURE;
} else {
dirEntryPtr =
(Fslcl_DirEntry *)((int)dirEntryPtr + dirEntryPtr->recordLength);
if (dirEntryPtr->nameLength != 2 ||
dirEntryPtr->fileName[0] != '.' ||
dirEntryPtr->fileName[1] != '.' ||
dirEntryPtr->fileName[2] != '\0') {
printf(
"GetParentNumber: \"..\", corrupted directory\n");
status = FAILURE;
} else {
*parentNumberPtr = dirEntryPtr->fileNumber;
}
}
Fscache_UnlockBlock(cacheBlockPtr, (time_t) 0,
-1, 0, FSCACHE_CLEAR_READ_AHEAD);
return(status);
}
/*
*----------------------------------------------------------------------
*
* SetParentNumber --
* Patch the file number for the ".." entry of a directory.
*
* Results:
* SUCCESS or an error code from the I/O.
*
* Side effects:
* Changes the parent file number of the ".." entry.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
SetParentNumber(curHandlePtr, newParentNumber)
Fsio_FileIOHandle *curHandlePtr; /* Handle for current directory */
int newParentNumber;/* The new file number of the parent
* of curHandlePtr */
{
ReturnStatus status;
int length;
register Fslcl_DirEntry *dirEntryPtr;
Fscache_Block *cacheBlockPtr;
status = Fscache_BlockRead(&curHandlePtr->cacheInfo, 0, &cacheBlockPtr,
&length, FSCACHE_DIR_BLOCK, FALSE);
if (status != SUCCESS) {
return(status);
} else if (length == 0) {
return(FAILURE);
}
dirEntryPtr = (Fslcl_DirEntry *)cacheBlockPtr->blockAddr;
if (dirEntryPtr->nameLength != 1 ||
dirEntryPtr->fileName[0] != '.' ||
dirEntryPtr->fileName[1] != '\0') {
printf(
"SetParentNumber: \".\", corrupted directory\n");
Fscache_UnlockBlock(cacheBlockPtr, (time_t) 0,
-1, 0, FSCACHE_CLEAR_READ_AHEAD);
return(FAILURE);
}
dirEntryPtr = (Fslcl_DirEntry *)((int)dirEntryPtr + dirEntryPtr->recordLength);
if (dirEntryPtr->nameLength != 2 ||
dirEntryPtr->fileName[0] != '.' ||
dirEntryPtr->fileName[1] != '.' ||
dirEntryPtr->fileName[2] != '\0') {
printf("SetParentNumber: \"..\", corrupted directory\n");
Fscache_UnlockBlock(cacheBlockPtr, (time_t) 0,
-1, 0, FSCACHE_CLEAR_READ_AHEAD);
return(FAILURE);
}
dirEntryPtr->fileNumber = newParentNumber;
status = CacheDirBlockWrite(curHandlePtr, cacheBlockPtr, 0, length);
return(status);
}
/*
*----------------------------------------------------------------------
*
* DeleteFileName --
*
* Delete a file by clearing its fileNumber in the directory and
* reducing its link count.
*
* Results:
* SUCCESS or an error code.
*
* Side effects:
* Log entry may be written.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
DeleteFileName(parentHandlePtr, curHandlePtr, component,
compLen, forRename, idPtr, logOp, clientID)
Fsio_FileIOHandle *parentHandlePtr; /* Handle of directory in
* which to delete file*/
Fsio_FileIOHandle *curHandlePtr; /* Handle of file to delete */
char *component; /* Name of the file to delte */
int compLen; /* The length of component */
int forRename; /* if FS_RENAME, then the file being delted
* is being renamed. This allows non-empty
* directories to be deleted */
Fs_UserIDs *idPtr; /* User and group IDs */
int logOp; /* Directory log operation.; */
int clientID;
{
ReturnStatus status;
Fsdm_FileDescriptor *parentDescPtr; /* Descriptor for parent */
Fsdm_FileDescriptor *curDescPtr; /* Descriptor for the file to delete */
int type; /* Type of the file */
int fileNumber; /* Number of file being deleted. */
ClientData logClientData; /* ClientData returned from
* DirOpStart. */
ClientData recovLogClientData = (ClientData) 0;
/* ClientData returned from
* DirOpStart, for recovery system. */
int dirOffset;
type = curHandlePtr->descPtr->fileType;
if (parentHandlePtr == (Fsio_FileIOHandle *)NIL) {
/*
* There is no handle on the parent because we have just
* gone up via "..". You can't delete the parent.
*/
status = FS_NO_ACCESS;
} else if ((compLen == 1) && (component[0] == '.')) {
/*
* Disallow removing dot.
*/
status = FS_NO_ACCESS;
} else if (type == FS_DIRECTORY && (!forRename) &&
!DirectoryEmpty(curHandlePtr)) {
status = FS_DIR_NOT_EMPTY;
} else {
/*
* One needs write permission in the parent to do the delete.
*/
status = CheckPermissions(parentHandlePtr, FS_WRITE, idPtr,
FS_DIRECTORY);
}
if (status != SUCCESS) {
return(status);
}
curDescPtr = curHandlePtr->descPtr;
parentDescPtr = parentHandlePtr->descPtr;
fileNumber = curHandlePtr->hdr.fileID.minor;
dirOffset = -1;
if (type == FS_DIRECTORY) {
logOp |= FSDM_LOG_IS_DIRECTORY;
}
logClientData = Fsdm_DirOpStart(logOp, parentHandlePtr, dirOffset,
component, compLen, fileNumber, type,
curHandlePtr->descPtr);
if (recov_Transparent && clientID != rpc_SpriteID) {
recovLogClientData = Fsrecov_DirOpStart(logOp, parentHandlePtr,
dirOffset, component, compLen, fileNumber, type,
curHandlePtr->descPtr);
}
/*
* Remove the name from the directory first.
*/
status = DeleteComponent(parentHandlePtr, component, compLen, &dirOffset);
if (status == SUCCESS) {
curDescPtr->numLinks--;
curDescPtr->flags |= FSDM_FD_LINKS_DIRTY;
if (type == FS_DIRECTORY) {
/*
* The directory might have been known in the hash table as
* someone's "..". Besure that this entry is gone.
*/
FSLCL_HASH_DELETE(fslclNameTablePtr, "..", curHandlePtr);
if (!forRename) {
/*
* Directories have an extra link because they reference
* themselves.
*/
curDescPtr->numLinks--;
if (curDescPtr->numLinks > 0) {
printf("DeleteFileName: extra links on directory\n");
}
}
}
curDescPtr->descModifyTime = Fsutil_TimeInSeconds();
status = Fsdm_FileDescStore(curHandlePtr, FALSE);
if (status != SUCCESS) {
printf("DeleteFileName: (1) Couldn't store descriptor\n");
#ifdef notdef
return(status);
#endif
}
if (type == FS_DIRECTORY) {
/*
* A directory's link count reflects the number of subdirectories
* it has (they each have a ".." that references it.) Here
* it is decremented because the subdirectory is going away.
*/
parentDescPtr->numLinks--;
parentDescPtr->descModifyTime = Fsutil_TimeInSeconds();
parentDescPtr->flags |= FSDM_FD_LINKS_DIRTY;
}
status = Fsdm_FileDescStore(parentHandlePtr, FALSE);
if (status != SUCCESS) {
printf("DeleteFileName: (2) Couldn't store descriptor\n");
#ifdef notdef
return(status);
#endif
}
if ((curDescPtr->numLinks <= 0) && (curHandlePtr->use.ref != 0)) {
logOp |= FSDM_LOG_STILL_OPEN;
}
}
Fsdm_DirOpEnd(logOp, parentHandlePtr, dirOffset,
component, compLen, fileNumber, type,
curDescPtr, logClientData, status);
if (recov_Transparent && clientID != rpc_SpriteID) {
Fsrecov_DirOpEnd(logOp, parentHandlePtr, dirOffset,
component, compLen, fileNumber, type,
curDescPtr, recovLogClientData, status);
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* CloseDeletedFile --
*
* This routine is called to close a deleted file.
* If there are no links left then the
* file's handle is marked as deleted. Finally, if this routine
* has the last reference on the handle then the file's data blocks
* are truncated away and the file descriptor is marked as free.
*
* This is a separate routine from DeleteFileName because the
* file cannot be closed unless the parent handle is unlocked
* (to prevent deadlock while doing the consistency callback),
* and the code to do a rename deletes the existing file, then
* does a link. This would break if the parent handle was
* released during the delete.
*
* Results:
* SUCCESS or error code.
*
* Side effects:
* The parent and/or current handles may be released and set to NIL.
*
*----------------------------------------------------------------------
*/
static void
CloseDeletedFile(parentHandlePtrPtr, curHandlePtrPtr)
Fsio_FileIOHandle **parentHandlePtrPtr; /* Handle of parent of
* deleted file. */
Fsio_FileIOHandle **curHandlePtrPtr; /* Handle of deleted file. */
{
register Fsio_FileIOHandle *curHandlePtr; /* Local copy */
curHandlePtr = *curHandlePtrPtr;
if (curHandlePtr->descPtr->numLinks <= 0) {
/*
* At this point curHandlePtr is potentially the last reference
* to the file. If there are no users then do the delete, otherwise
* mark the handle as deleted and Fsio_FileClose will take care of
* it.
*/
curHandlePtr->flags |= FSIO_FILE_NAME_DELETED;
if (curHandlePtr->use.ref == 0) {
/*
* Handle the deletion and clean up the handle.
* We set the the clientID to us and specify client
* call-backs so that any other clients will be notified.
*/
Fsutil_HandleRelease(*parentHandlePtrPtr, TRUE);
*parentHandlePtrPtr = (Fsio_FileIOHandle *)NIL;
(void)Fsio_FileCloseInt(curHandlePtr, 0, 0, 0, rpc_SpriteID,
TRUE);
*curHandlePtrPtr = (Fsio_FileIOHandle *)NIL;
} else {
Fsutil_HandleRelease(curHandlePtr, TRUE);
*curHandlePtrPtr = (Fsio_FileIOHandle *)NIL;
}
} else {
Fsutil_HandleRelease(curHandlePtr, TRUE);
*curHandlePtrPtr = (Fsio_FileIOHandle *)NIL;
}
}
/*
*----------------------------------------------------------------------
*
* Fslcl_DeleteFileDesc --
* Delete a file from disk given its file handle. It is assumed that the
* file's name has already been deleted from the directory structure.
*
* Results:
* None.
*
* Side effects:
* Delete the data blocks and mark file descriptor as free. The
* handle remains locked during this call. The DESCRIPTOR referenced
* by the handle is FREED because the file handle is going to
* be removed.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Fslcl_DeleteFileDesc(handlePtr)
Fsio_FileIOHandle *handlePtr;
{
ReturnStatus status;
Fsdm_Domain *domainPtr;
if (handlePtr->descPtr->fileType == FS_DIRECTORY) {
/*
* Remove .. from the name cache so we don't end up with
* a bad cache entry later when this directory is re-created.
*/
FSLCL_HASH_DELETE(fslclNameTablePtr, "..", handlePtr);
}
domainPtr = Fsdm_DomainFetch(handlePtr->hdr.fileID.major, FALSE);
if (domainPtr == (Fsdm_Domain *)NIL) {
return(FS_DOMAIN_UNAVAILABLE);
}
/*
* The ordering of the deletion is as follows:
* 1. Mark the descriptor on disk as free so if we crash the
* disk scavenger will free the blocks for us.
* 2. Truncate the blocks out of the cache and from the descriptor.
* 3. Mark the file descriptor as available in the bitmask.
*/
handlePtr->descPtr->flags = FSDM_FD_FREE | FSDM_FD_DIRTY;
status = Fsdm_FileDescStore(handlePtr,TRUE);
if (status != SUCCESS) {
printf("Fslcl_DeleteFileDesc: Can't mark descriptor as free\n");
} else {
status = Fsio_FileTrunc(handlePtr, 0, FSCACHE_TRUNC_DELETE);
if (status != SUCCESS) {
printf("Fslcl_DeleteFileDesc: Can't truncate file <%d,%d> \"%s\"\n",
handlePtr->hdr.fileID.major, handlePtr->hdr.fileID.minor,
Fsutil_HandleName(handlePtr));
} else {
(void)Fsdm_FreeFileNumber(domainPtr, handlePtr->hdr.fileID.minor);
}
free((Address)handlePtr->descPtr);
handlePtr->descPtr = (Fsdm_FileDescriptor *)NIL;
}
Fsdm_DomainRelease(handlePtr->hdr.fileID.major);
return(status);
}
/*
*----------------------------------------------------------------------
*
* DirectoryEmpty --
* Scan a directory and determine if there are any files left
* in it other than "." and "..".
*
* Results:
* TRUE if the directory is empty, FALSE otherwise.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static Boolean
DirectoryEmpty(handlePtr)
Fsio_FileIOHandle *handlePtr; /* Handle of directory to check */
{
ReturnStatus status;
int blockOffset; /* Offset within a directory block */
Fslcl_DirEntry *dirEntryPtr; /* Reference to directory entry */
int length; /* Length for read call */
int dirBlockNum;
Fscache_Block *cacheBlockPtr;
dirBlockNum = 0;
do {
status = Fscache_BlockRead(&handlePtr->cacheInfo, dirBlockNum,
&cacheBlockPtr, &length, FSCACHE_DIR_BLOCK, FALSE);
if (status != SUCCESS || length == 0) {
/*
* Have run out of the directory and not found anything.
*/
return(TRUE);
}
blockOffset = 0;
dirEntryPtr = (Fslcl_DirEntry *)cacheBlockPtr->blockAddr;
while (blockOffset < length) {
if (dirEntryPtr->fileNumber != 0) {
/*
* A valid directory record.
*/
if ((strcmp(".", dirEntryPtr->fileName) == 0) ||
(strcmp("..", dirEntryPtr->fileName) == 0)) {
/*
* "." and ".." are ok
*/
} else {
Fscache_UnlockBlock(cacheBlockPtr, (time_t) 0,
-1, 0, FSCACHE_CLEAR_READ_AHEAD);
return(FALSE);
}
}
blockOffset += dirEntryPtr->recordLength;
dirEntryPtr =
(Fslcl_DirEntry *)((int)dirEntryPtr + dirEntryPtr->recordLength);
}
dirBlockNum++;
Fscache_UnlockBlock(cacheBlockPtr, (time_t) 0,
-1, 0, FSCACHE_CLEAR_READ_AHEAD);
} while(TRUE);
/*NOTREACHED*/
}
/*
*----------------------------------------------------------------------
*
* CheckPermissions --
*
* Check permissions on a file during lookup. This just looks
* at the uid, groupIDs, and the permission bits on the file.
*
* Some semantic checking is done:
* type indicates what kind of file to accept.
* Execution of files with no execute bit is prevented for uid = 0
* Execution of directories is prevented.
* Some semantic checking is not done:
* Doesn't check against writing to directories. This is
* done later in the FileSrvOpen routine.
*
* Results:
* FS_NO_ACCESS if the useFlags include a permission that does
* not fit with the uid/groupIDs of the file.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
CheckPermissions(handlePtr, useFlags, idPtr, type)
Fsio_FileIOHandle *handlePtr;
register int useFlags;
register Fs_UserIDs *idPtr;
int type;
{
register Fsdm_FileDescriptor *descPtr;
register int *groupPtr;
register unsigned int permBits;
register int index;
register int uid = idPtr->user;
ReturnStatus status;
if (handlePtr->hdr.fileID.type != FSIO_LCL_FILE_STREAM) {
panic( "CheckPermissions on non-local file\n");
return(FAILURE);
}
descPtr = handlePtr->descPtr;
/*
* Make sure the file type matches. FS_FILE means any type, otherwise
* it should match exactly.
*/
if (type != FS_FILE && type != descPtr->fileType) {
/*
* Patch around the fact that FS_REMOTE_LINK and FS_SYMBOLIC_LINK
* get or'ed together, but they are not-proper bit fields.
* (They equal 3 and 2, respectively.)
* Hence we allow a regular symbolic link to satisfy a
* request for a remote link.
*/
if ((type == (FS_REMOTE_LINK|FS_SYMBOLIC_LINK)) &&
((descPtr->fileType == FS_SYMBOLIC_LINK) ||
(descPtr->fileType == FS_REMOTE_LINK))) {
/* printf( "Allowing a symlink for a remote link\n");*/
} else {
switch(type) {
case FS_DIRECTORY:
return(FS_NOT_DIRECTORY);
default:
return(FS_WRONG_TYPE);
}
}
}
/*
* Dis-allow execution of directories...
*/
if ((type == FS_FILE) && (useFlags & FS_EXECUTE) &&
(descPtr->fileType != FS_FILE)) {
return(FS_WRONG_TYPE);
}
/*
* Reset SETUID/SETGID bit when writing a file.
*/
if ((useFlags & FS_WRITE) && (descPtr->fileType == FS_FILE) &&
(descPtr->permissions & (FS_SET_UID|FS_SET_GID))) {
descPtr->permissions &= ~(FS_SET_UID|FS_SET_GID);
descPtr->flags |= FSDM_FD_PERMISSIONS_DIRTY;
}
/*
* Check for ownership permission. This probably redundant with
* respect to the checking done by FslclSetAttr.
*/
#ifdef notdef
if (useFlags & FS_OWNERSHIP) {
if ((uid != descPtr->uid) && (uid != 0)) {
return(FS_NOT_OWNER);
}
}
#endif notdef
/*
* Check read/write/exec permissions against one of the owner bits,
* the group bits, or the world bits. 'permBits' is set to
* be the corresponding bits from the file descriptor and then
* shifted over so the comparisions are against the WORLD bits.
*/
if (uid == 0) {
/*
* For normal files, only check for execute permission. This
* prevents root from being able to execute ordinary files by
* accident. However, root has complete access to directories.
*/
if (descPtr->fileType == FS_DIRECTORY) {
return(SUCCESS);
}
useFlags &= FS_EXECUTE;
}
if (uid == descPtr->uid) {
permBits = (descPtr->permissions >> 6) & 07;
} else {
for (index = idPtr->numGroupIDs, groupPtr = idPtr->group;
index > 0;
index--, groupPtr++) {
if (*groupPtr == descPtr->gid) {
permBits = (descPtr->permissions >> 3) & 07;
goto havePermBits;
}
}
permBits = descPtr->permissions & 07;
}
havePermBits:
if (((useFlags & FS_READ) && ((permBits & FS_WORLD_READ) == 0)) ||
((useFlags & FS_WRITE) && ((permBits & FS_WORLD_WRITE) == 0)) ||
((useFlags & FS_EXECUTE) && ((permBits & FS_WORLD_EXEC) == 0))) {
/*
* The file's permission don't include what is needed.
*/
status = FS_NO_ACCESS;
} else {
status = SUCCESS;
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* CacheDirBlockWrite --
*
* Write into a cache block returned from Fscache_BlockRead. Used only
* for writing directories.
*
* Results:
* SUCCESS unless error when allocating disk space.
*
* Side effects:
* The cache block is unlocked. It is deleted if the offset is the
* beginning of the block and the disk allocation failed.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
CacheDirBlockWrite(handlePtr, blockPtr, blockNum, length)
register Fsio_FileIOHandle *handlePtr; /* Handle for file. */
register Fscache_Block *blockPtr; /* Cache block. */
int blockNum; /* Block number. */
int length; /* Number of valid bytes in
* the block. */
{
ReturnStatus status = SUCCESS;
int blockAddr = FSDM_NIL_INDEX;
Boolean newBlock;
int flags = FSCACHE_CLEAR_READ_AHEAD;
int offset;
int newLastByte;
int blockSize;
offset = blockNum * FS_BLOCK_SIZE;
newLastByte = offset + length - 1;
(void) (handlePtr->cacheInfo.backendPtr->ioProcs.allocate)
((Fs_HandleHeader *)handlePtr, offset, length, 0,
&blockAddr, &newBlock);
#ifdef lint
(void) Fsdm_BlockAllocate((Fs_HandleHeader *)handlePtr, offset, length,
0, &blockAddr, &newBlock);
(void) FsrmtFileBlockAllocate((Fs_HandleHeader *)handlePtr, offset, length,
0, &blockAddr, &newBlock);
#endif /* lint */
if (blockAddr == FSDM_NIL_INDEX) {
status = FS_NO_DISK_SPACE;
printf("CacheDirBlockWrite: out of space for %s.\n",
Fsutil_HandleName(handlePtr)); /* DEBUG */
if (handlePtr->descPtr->lastByte + 1 < offset) {
/*
* Delete the block if are appending and this was a new cache
* block.
*/
flags = FSCACHE_DELETE_BLOCK;
}
}
Fscache_UpdateDirSize(&handlePtr->cacheInfo, newLastByte);
handlePtr->descPtr->dataModifyTime = Fsutil_TimeInSeconds();
handlePtr->descPtr->descModifyTime = handlePtr->descPtr->dataModifyTime;
handlePtr->descPtr->flags |= FSDM_FD_MODTIME_DIRTY;
fs_Stats.blockCache.dirBytesWritten += FSLCL_DIR_BLOCK_SIZE;
fs_Stats.blockCache.dirBlockWrites++;
blockSize = handlePtr->descPtr->lastByte + 1 - (blockNum * FS_BLOCK_SIZE);
if (blockSize > FS_BLOCK_SIZE) {
blockSize = FS_BLOCK_SIZE;
}
Fscache_UnlockBlock(blockPtr, (time_t) Fsutil_TimeInSeconds(), blockAddr,
blockSize, flags);
return(status);
}
/*
*----------------------------------------------------------------------
*
* Fslcl_CheckDirLog --
*
* Check the directory op log against the contents of a dir. If there's
* a discrepancy, modify the directory to contain the correct files
* and directories.
*
* Results:
* None.
*
* Side effects:
* Modify the directory to contain the correct files.
*
*----------------------------------------------------------------------
*/
void
Fslcl_CheckDirLog(parentHandlePtr, dirLogList)
Fsio_FileIOHandle *parentHandlePtr;
List_Links *dirLogList;
{
List_Links *itemPtr;
char *component;
int compLen;
FslclHashEntry *entryPtr; /* Name cache entry */
Fsio_FileIOHandle *handlePtr;
Boolean found;
int dirBlockNum;
ReturnStatus status;
Fscache_Block *cacheBlockPtr; /* Cache block */
int length;
int blockOffset;
Fslcl_DirEntry *dirEntryPtr; /* Reference to directory entry */
char buf[256];
if (!recov_Transparent) {
panic("Fslcl_CheckDirLog: shouldn't have been called.\n");
}
LIST_FORALL(dirLogList, itemPtr) {
/* Try cached lookup first. */
found = FALSE;
Fsrecov_GetComponent(itemPtr, &component, &compLen);
strncpy(buf, component, compLen);
buf[compLen] = '\0';
entryPtr =
FSLCL_HASH_LOOK_ONLY(fslclNameTablePtr, component, parentHandlePtr);
if (entryPtr != (FslclHashEntry *)NIL) {
if (entryPtr->hdrPtr->fileID.type != FSIO_LCL_FILE_STREAM) {
panic("Fslcl_CheckDirLog: got trashy handle from cache");
}
handlePtr = (Fsio_FileIOHandle *) entryPtr->hdrPtr;
found = TRUE;
} else {
dirBlockNum = 0;
do {
status = Fscache_BlockRead(&parentHandlePtr->cacheInfo,
dirBlockNum, &cacheBlockPtr, &length, FSCACHE_DIR_BLOCK,
FALSE);
if (status != SUCCESS || length == 0) {
break;
}
dirEntryPtr = (Fslcl_DirEntry *)cacheBlockPtr->blockAddr;
blockOffset = 0;
while (blockOffset < length) {
if (dirEntryPtr->recordLength <= 0) {
printf(" File ID <%d, %d, %d>",
parentHandlePtr->hdr.fileID.serverID,
parentHandlePtr->hdr.fileID.major,
parentHandlePtr->hdr.fileID.minor);
printf(" dirBlockNum <%d>, blockOffset <%d>",
dirBlockNum, blockOffset);
printf("\n");
Fscache_UnlockBlock(cacheBlockPtr, (time_t) 0,
-1, 0, FSCACHE_CLEAR_READ_AHEAD);
panic("Fslcl_CheckDirLog: Corrupted directory?");
}
if (dirEntryPtr->fileNumber != 0) {
/*
* A valid directory record. If component and the directory
* entry are the same length then compare them for a match.
* This String Compare is in-lined for efficiency.
*/
if ((dirEntryPtr->nameLength == compLen)) {
if (strncmp(component, dirEntryPtr->fileName,
compLen) == 0) {
found = TRUE;
break;
}
}
} else {
printf("Uh oh, dirEntryPtr->fileNumber is 0\n");
}
blockOffset += dirEntryPtr->recordLength;
dirEntryPtr = (Fslcl_DirEntry *)((int)dirEntryPtr +
dirEntryPtr->recordLength);
}
dirBlockNum++;
Fscache_UnlockBlock(cacheBlockPtr, (time_t) 0, -1, 0, 0);
} while(!found);
}
/* It's been found or not. Now deal with the result. */
if (found) {
printf("Found %s\n", component);
} else {
printf("Didn't find %s\n", component);
}
}
return;
}